﻿using System;
using System.IO;
using System.Xml;
using System.Reflection;
using System.Xml.Linq;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;
using System.Windows;

namespace GestureControl
{
    /// <summary>
    /// Take a list of points and try to recognize a pattern
    /// </summary>
    public class SurfaceGesture : List<SurfaceGestureVector>
    {
        #region Prop
        /// <summary>
        /// Allow some variation without log a direction
        /// </summary>
        public int Precision { get; set; }
        /// <summary>
        /// List of pattern readed in the patterns.xml
        /// </summary>
        public List<SurfaceGesturePattern> Pattern = new List<SurfaceGesturePattern>();
        #endregion
        #region Constructor
        /// <summary>
        /// load know patterns and generate the vector list from a list of points with a default 20 precision factor
        /// </summary>
        /// <param name="list"></param>
        public SurfaceGesture(List<SurfaceGesturePoint> list)
        {
            this.Precision = 20;
            CommonPattern();
            this.Generate(list);
        }
        /// <summary>
        /// load know patterns and generate the vector list from a list of points with a precision factor
        /// </summary>
        /// <param name="list"></param>
        /// <param name="precision"></param>
        public SurfaceGesture(List<SurfaceGesturePoint> list, int precision)
        {
            this.Precision = precision;
            CommonPattern();
            this.Generate(list);
        }
        //public SurfaceGesture(System.Windows.Ink.Stroke stroke, int precision)
        //{
        //    StylusPointCollection tmp = stroke.GetBezierStylusPoints();
        //    List<SurfaceGesturePoint> pointList = new List<SurfaceGesturePoint>();
        //    foreach (StylusPoint p in tmp)
        //        pointList.Add(new SurfaceGesturePoint { X = p.X, Y = p.Y });
        //    this.Precision = precision;
        //    this.Generate(pointList);
        //    CommonPattern();
        //}
        #endregion
        #region GenerateVector
        /// <summary>
        /// Generate list of vector from courbe mouvements, filter with the pas value
        /// </summary>
        /// <param name="list"></param>
        /// <param name="pas"></param>
        /// <returns></returns>
        private bool GenerateCourb(List<SurfaceGesturePoint> list, int pas)
        {
            List<SurfaceGesturePattern> tmp = new List<SurfaceGesturePattern>();
            int sep = list.Count / pas;
            double count = 0; ;
            SurfaceGesturePoint past = new SurfaceGesturePoint() { X = 0, Y = 0 };
            double y = 0;

            for (int i = 0; i < list.Count - 1; i++)
            {
                if (i % pas != 0)
                {
                    count += Math.Atan(list[i + 1].Y / list[i + 1].X) - Math.Atan(list[i].Y / list[i].X);
                }
                else
                {
                    count /= pas;
                    if (count == 0 || this.GetDistancePoints(past, list[i + 1]) < 5)
                    {
                        y = list[i + 1].Y;
                        past.X = count;
                        continue;
                    }
                    if (y > list[i + 1].Y)
                    {
                        if (past.X > count)
                            this.AddDirection(SurfaceGestureVectorDirection.UPRIGHT);
                        else
                            this.AddDirection(SurfaceGestureVectorDirection.UPLEFT);
                    }
                    else
                    {
                        if (past.X > count)
                            this.AddDirection(SurfaceGestureVectorDirection.DOWNRIGHT);
                        else
                            this.AddDirection(SurfaceGestureVectorDirection.DOWNLEFT);
                    }
                    y = list[i + 1].Y;
                    past.X = count;
                }
            }
            Console.Write(this);
            if (this.GetPattern() != "None")
                return true;
            else
                return false;
        }
        /// <summary>
        /// Get distance between two points
        /// </summary>
        /// <param name="p1"></param>
        /// <param name="p2"></param>
        /// <returns></returns>
        private int GetDistancePoints(SurfaceGesturePoint p1, SurfaceGesturePoint p2)
        {
            return (int)Math.Sqrt(Math.Pow((p2.X - p1.X), 2) + Math.Pow((p1.Y - p2.Y), 2));
        }
        /// <summary>
        /// add a direction in the vector list if past who not the same
        /// </summary>
        /// <param name="type"></param>
        private void AddDirection(SurfaceGestureVectorDirection type)
        {
            if (this.Count == 0)
            {
                this.Add(new SurfaceGestureVector{Direction = type, Lenght=42});
                return ;
            }
            if (this[this.Count - 1].Direction != type)
                this.Add(new SurfaceGestureVector { Direction = type, Lenght = 42 });
        }
        /// <summary>
        /// generate list of vector
        /// </summary>
        /// <param name="list"></param>
        private void Generate(List<SurfaceGesturePoint> list)
        {
            if (list.Count < 2)
                return ;
            SurfaceGestureVectorDirection lastDirection = SurfaceGestureVectorDirection.NONE;
            int lastPoint = 0;
            SurfaceGesturePoint LastChange = list[0];

            /////// TEST///////////
            if (this.GenerateCourb(list, 5) == true)
                return;
            this.Clear();
            ///////////////////////

            for (int i = 0; i < list.Count - 1; i++)
            {
                if (GetHorizontal(list[lastPoint], list[i + 1]) == SurfaceGestureVectorDirection.UP && lastDirection != SurfaceGestureVectorDirection.UP)
                {
                    this.Add(new SurfaceGestureVector { Direction = SurfaceGestureVectorDirection.UP, Lenght = (Math.Abs(LastChange.Y - list[i + 1].Y)) });
                    LastChange = list[i + 1];
                    lastDirection = SurfaceGestureVectorDirection.UP;
                }
                else if (GetHorizontal(list[lastPoint], list[i + 1]) == SurfaceGestureVectorDirection.DOWN && lastDirection != SurfaceGestureVectorDirection.DOWN)
                {
                    this.Add(new SurfaceGestureVector { Direction = SurfaceGestureVectorDirection.DOWN, Lenght = (Math.Abs(LastChange.Y - list[i + 1].Y)) });
                    LastChange = list[i + 1];
                    lastDirection = SurfaceGestureVectorDirection.DOWN;
                }
                else if (GetVertical(list[lastPoint], list[i + 1]) == SurfaceGestureVectorDirection.LEFT && lastDirection != SurfaceGestureVectorDirection.LEFT)
                {
                    this.Add(new SurfaceGestureVector { Direction = SurfaceGestureVectorDirection.LEFT, Lenght = (Math.Abs(LastChange.X - list[i + 1].X)) });
                    LastChange = list[i + 1];
                    lastDirection = SurfaceGestureVectorDirection.LEFT;
                }
                else if (GetVertical(list[lastPoint], list[i + 1]) == SurfaceGestureVectorDirection.RIGHT && lastDirection != SurfaceGestureVectorDirection.RIGHT)
                {
                    this.Add(new SurfaceGestureVector { Direction = SurfaceGestureVectorDirection.RIGHT, Lenght = (Math.Abs(LastChange.X - list[i + 1].X)) });
                    LastChange = list[i + 1];
                    lastDirection = SurfaceGestureVectorDirection.RIGHT;
                }
                ++lastPoint;
            }
        }
        private SurfaceGestureVectorDirection GetHorizontal(SurfaceGesturePoint p1, SurfaceGesturePoint p2)
        {
            if (p1.Y < p2.Y)
            {
                // go up
                if (Math.Abs(p2.Y - p1.Y) > this.Precision && Math.Abs(p2.X - p1.X) < Math.Abs(p2.Y - p1.Y))
                    return SurfaceGestureVectorDirection.DOWN;
            }
            else
            {
                // go down
                if (Math.Abs(p1.Y - p2.Y) > this.Precision && Math.Abs(p1.X - p2.X) < Math.Abs(p1.Y - p2.Y))
                    return SurfaceGestureVectorDirection.UP;
            }
            return SurfaceGestureVectorDirection.NONE;
        }
        private SurfaceGestureVectorDirection GetVertical(SurfaceGesturePoint p1, SurfaceGesturePoint p2)
        {
            if (p1.X < p2.X)
            {
                // go left
                if (Math.Abs(p2.X - p1.X) > this.Precision && Math.Abs(p2.Y - p1.Y) < Math.Abs(p2.X - p1.X))
                    return SurfaceGestureVectorDirection.RIGHT;
            }
            else
            {
                // go right
                if (Math.Abs(p1.X - p2.X) > this.Precision && Math.Abs(p1.Y - p2.Y) < Math.Abs(p1.X - p2.X))
                    return SurfaceGestureVectorDirection.LEFT;
            }
            return SurfaceGestureVectorDirection.NONE;
        }
        #endregion
        #region Override
        public override String ToString()
        {
            String ret = "";

            foreach (SurfaceGestureVector v in this)
            {
                ret += (v.Direction + ", lenght:" + v.Lenght + ", precision:" + this.Precision + "\n");
                //Console.WriteLine(v.Direction + ", lenght:" + v.Lenght + ", precision:" + this.Precision);
            }
            return ret;
        }
        #endregion
        #region Pattern
        /// <summary>
        /// return a String with the recognized pattern, "None" if no pattern
        /// </summary>
        /// <returns></returns>
        public String GetPattern()
        {
            foreach (SurfaceGesturePattern p in this.Pattern)
            {
                if (p.Count == this.Count)
                {
                    int i;
                    for (i = 0; i < p.Count; i++)
                    {
                        if (this[i].Direction != p[i].Direction)
                            break ;
                    }
                    if (i == p.Count)
                        return p.Name;
                }
            }
            return "None";
        }
        /// <summary>
        /// Load know patterns from the Resources/Patterns.xml file
        /// </summary>
        private void CommonPattern()
        {
            try
            {
                #region Load Patterns
                System.IO.Stream file = Assembly.GetExecutingAssembly().GetManifestResourceStream("GestureControl.Resources.Patterns.xml");
                XmlDocument xml = new XmlDocument();
                xml.Load(file);
                XmlElement root = xml.DocumentElement;
                XmlNodeList nodes = root.SelectNodes("//Pattern");
                SurfaceGesturePattern p;
                int i = 0;
                foreach (XmlNode node in nodes)
                {
                    string name = node["Name"].InnerText;
                    XmlNodeList subNodes = node.SelectNodes("//Directions");
                    if (subNodes == null)
                        continue;
                    p = new SurfaceGesturePattern() { Name = name };
                    foreach (XmlNode subNode in subNodes[i++])
                    {
                        XmlNodeList dl = subNode.ChildNodes;
                        foreach (XmlNode d in dl)
                        {
                            switch (d.InnerText)
                            {
                                case "Up":
                                    p.Add(new SurfaceGestureVector() { Direction = SurfaceGestureVectorDirection.UP });
                                    break;
                                case "Down":
                                    p.Add(new SurfaceGestureVector() { Direction = SurfaceGestureVectorDirection.DOWN });
                                    break;
                                case "Left":
                                    p.Add(new SurfaceGestureVector() { Direction = SurfaceGestureVectorDirection.LEFT });
                                    break;
                                case "Right":
                                    p.Add(new SurfaceGestureVector() { Direction = SurfaceGestureVectorDirection.RIGHT });
                                    break;
                                case "DownRight":
                                    p.Add(new SurfaceGestureVector() { Direction = SurfaceGestureVectorDirection.DOWNRIGHT });
                                    break;
                                case "DownLeft":
                                    p.Add(new SurfaceGestureVector() { Direction = SurfaceGestureVectorDirection.DOWNLEFT });
                                    break;
                                case "UpRight":
                                    p.Add(new SurfaceGestureVector() { Direction = SurfaceGestureVectorDirection.UPRIGHT });
                                    break;
                                case "UpLeft":
                                    p.Add(new SurfaceGestureVector() { Direction = SurfaceGestureVectorDirection.UPLEFT });
                                    break;
                                default:
                                    break;
                            }
                        }
                    }
                    this.Pattern.Add(p);
                }
                #endregion
            }
            catch
            {
                throw new Exception("Error loading Patterns.xml");
            }
        }
        #endregion
    }
    #region Tools
    /// <summary>
    /// Gesture event who return a object with a Gesture String value
    /// </summary>
    public class GestureRoutedEventArgs : RoutedEventArgs
    {
        public String Gesture { get; private set; }
            
        public GestureRoutedEventArgs() : base() { }
        public GestureRoutedEventArgs(String gesture) : base() { this.Gesture = gesture; }
        public GestureRoutedEventArgs(RoutedEvent routedEvent) : base(routedEvent) { }
        public GestureRoutedEventArgs(RoutedEvent routedEvent, String gesture) : base(routedEvent) { this.Gesture = gesture; }
        public GestureRoutedEventArgs(RoutedEvent routedEvent, Object source) : base(routedEvent, source) { }
        public GestureRoutedEventArgs(RoutedEvent routedEvent, Object source, String gesture) : base(routedEvent, source) { this.Gesture = gesture; }
    }

    public class SurfaceGesturePattern : List<SurfaceGestureVector>
    {
        public String Name { get; set; }

    }
    /// <summary>
    /// Possible Gesture Vector
    /// </summary>
    public enum SurfaceGestureVectorDirection
    {
        UP,
        DOWN,
        LEFT,
        RIGHT,
        UPLEFT,
        UPRIGHT,
        DOWNLEFT,
        DOWNRIGHT,
        NONE,
    }
    /// <summary>
    /// Describe a point in the grid
    /// </summary>
    public class SurfaceGesturePoint
    {
        public double X { get; set; }
        public double Y { get; set; }
    }

    /// <summary>
    /// Describe a vector to help the recognize pass
    /// </summary>
    public class SurfaceGestureVector
    {
        public SurfaceGestureVectorDirection Direction { get; set; }
        public Double Lenght { get; set; }
    }
    #endregion
}
